<script lang="ts" setup>
import { ref, shallowRef, nextTick, inject } from 'vue';
import i18n from '../../common/i18n';
import { Message, MessageTreeData, MessageValue, TreeMenuItem } from '../../common/types';
import { MessageTreeDataType, SecsFormat } from '../../common/enums';
import helper from '../../common/helpler';
import TreeDataTypeSvgVue from '../../components/TreeDataTypeSvg.vue';
import Node from "element-plus/lib/components/tree/src/model/node.js";
import ContextMenuVue from '../../components/ContextMenu.vue';

const convertMode = ref('1');
const txtText = ref("");
const treeData = ref<MessageTreeData[]>([]);
const txtReg = ref('<(L|BL|Boolean|BOOLEAN|B|A|J|I8|I1|I2|I4|F8|F4|U8|U1|U2|U4)([\\s\\S]+)(\\s+)([\\s\\S]*)\\s?>');
const reg2 = new RegExp("<(L|BL|Boolean|BOOLEAN|B|A|J|I8|I1|I2|I4|F8|F4|U8|U1|U2|U4)(\\s?)([\\s\\S]*)\\s?>");
const uiTree = shallowRef();
const setCopiedData = inject<(data: MessageTreeData | null, isCut: boolean) => void>("setCopiedData");
const txtError = ref('');

class Lines {
    data: string[];
    index: number;

    constructor(data: string[], index: number) {
        this.data = data;
        this.index = index;
    }

    next(): string | null {
        if (this.index > this.data.length - 1) {
            return null;
        }
        let result = this.data[this.index];
        this.index += 1;
        return result;
    }
}
const convert = () => {
    treeData.value.splice(0, 1);
    let txt = txtText.value.trim();
    if (txt.length == 0) {
        txtError.value = "the input is empty";
        return;
    }
    txtError.value = "";
    const reg = new RegExp(txtReg.value);
    let lines = txt.split(/\r?\n/g);
    if (lines.length == 0) {
        return;
    }
    let message = null as Message | null;
    let strLine = lines[0].trim();
    let firstLineIndex = 0;
    try {
        if (!strLine.startsWith("<") && !strLine.startsWith(">")) {
            let reg = /S(\d+)F(\d+)[\s\S]*(W?)[\s\S]*/;
            let match = reg.exec(strLine);
            if (match != null) {
                firstLineIndex = 1;
                let s = match[1];
                let f = match[2];
                let w = match[3];
                message = {
                    name: `S${s}F${f}`,
                    desc: '',
                    head: {
                        s: parseInt(s),
                        f: parseInt(f),
                        systemByte: 0,
                        isExpectReply: w == "W"
                    },
                    body: null
                };
            }
        }
        // 判断第一行是不是消息头
        if (lines.length - 1 < firstLineIndex) {
            return;
        }
        let dataSource = new Lines(lines, firstLineIndex);
        let value = recuriseRead(reg, null, 0, dataSource);
        let currentTreeData = null;
        if (message != null) {
            message.body = value == null ? null : value;
            currentTreeData = helper.parseToMessageTreeData(message, MessageTreeDataType.Primary);
        }
        else if (value != null) {
            currentTreeData = helper.analysisMessageValue(value, true);
        }
        else {
            throw new Error("input is invalid");
        }
        if (currentTreeData != null) {
            treeData.value = [currentTreeData];
            nextTick(() => {
                const node = uiTree.value?.getNode(currentTreeData.id);
                if (node != null) {
                    helper.expandNode(node, true);
                }
            });
        }
    }
    catch (error) {
        txtError.value = error as any;
    }

}

const recuriseRead = (reg: RegExp, parent: MessageValue | null, depth: number, dataSource: Lines) => {
    while (true) {
        let line = dataSource.next();
        if (line == null) {
            return null;
        }
        line = line.trim();
        if (line.startsWith(">")) {
            return null;
        }
        if (line.startsWith("<L")) {
            let item = {
                name: "",
                desc: "",
                format: SecsFormat.List,
                value: [],
                mappingRef: null
            };
            recuriseRead(reg, item, depth + 1, dataSource);
            if (parent == null) {
                return item;
            }
            else {
                parent.value.push(item);
            }
        }
        else {
            let match = reg.exec(line);
            if (match != null) {
                let item = parseToValueItem(match[1], match[4]);
                if (parent == null) {
                    return item;
                }
                else {
                    parent.value.push(item);
                }
            }
            else {
                match = reg2.exec(line);
                if (match != null) {
                    let item = parseToValueItem(match[1], match[3]);
                    if (parent == null) {
                        return item;
                    }
                    else {
                        parent.value.push(item);
                    }
                }
            }
        }
    }
}

const parseToValueItem = (strFormat: string, strValue: string): MessageValue => {
    switch (strFormat) {
        case "B": {
            return parseToValue(strValue, SecsFormat.Binary);
        }
        case "BL":
        case "Boolean":
        case "BOOLEAN": {
            return parseToValue(strValue, SecsFormat.Boolean);
        }
        case "A": {
            let isStartWithS = strValue.startsWith("'") || strValue.startsWith("\"");
            let isEndWithS = strValue.endsWith("'") || strValue.endsWith("\"");
            if (isStartWithS && isEndWithS && strValue.length > 1) {
                strValue = strValue.substring(1, strValue.length - 1);
            }
            return {
                name: "",
                desc: "",
                format: SecsFormat.AscII,
                value: strValue,
                mappingRef: null
            };
        }
        case "J": {
            let isStartWithS = strValue.startsWith("'") || strValue.startsWith("\"");
            let isEndWithS = strValue.endsWith("'") || strValue.endsWith("\"");
            if (isStartWithS && isEndWithS && strValue.length > 1) {
                strValue = strValue.substring(1, strValue.length - 1);
            }
            return {
                name: "",
                desc: "",
                format: SecsFormat.Jis8,
                value: strValue,
                mappingRef: null
            };
        }
        case "F8": {
            return parseToValue(strValue, SecsFormat.F8);
        }
        case "F4": {
            return parseToValue(strValue, SecsFormat.F4);
        }
        case "I8": {
            return parseToValue(strValue, SecsFormat.I8);
        }
        case "I4": {
            return parseToValue(strValue, SecsFormat.I4);
        }
        case "I2": {
            return parseToValue(strValue, SecsFormat.I2);
        }
        case "I1": {
            return parseToValue(strValue, SecsFormat.I1);
        }
        case "U8": {
            return parseToValue(strValue, SecsFormat.U8);
        }
        case "U4": {
            return parseToValue(strValue, SecsFormat.U4);
        }
        case "U2": {
            return parseToValue(strValue, SecsFormat.U2);
        }
        case "U1": {
            return parseToValue(strValue, SecsFormat.U1);
        }
        default: {
            throw new Error(`the format = ${strFormat} is invalid`);
        }
    }
}

const parseToValue = (strValue: string, format: SecsFormat): MessageValue => {
    if (strValue.startsWith("[") && strValue.endsWith("]")) {
        strValue = strValue.substring(1, strValue.length - 1);
    }
    return {
        name: "",
        desc: "",
        format: format,
        value: helper.parseStringToValue(strValue, format),
        mappingRef: null
    };
}

const defaultProps = {
    children: 'children',
    label: 'label'
}

let currentTreeNode: Node | null = null;
const contextMenuRef = shallowRef();
const contextMenuItems: TreeMenuItem[] = [
    {
        label: 'Copy',
        isEnable: () => {
            if (currentTreeNode?.data.tag.type == MessageTreeDataType.RootValue || currentTreeNode?.data.tag.type == MessageTreeDataType.Value) {
                return true;
            }
            return false
        },
        clickHandler: () => {
            if (currentTreeNode == null) {
                return;
            }
            setCopiedData && setCopiedData(helper.duplicateTreeData(currentTreeNode.data as MessageTreeData, false)!, false);
        }
    }
]

const handleContextMenu = (evt: PointerEvent, _data: MessageTreeData, node: Node) => {
    currentTreeNode = node;
    contextMenuRef.value.show(evt.clientX, evt.clientY);
}

</script>
<template>
    <el-container>
        <el-header class="app-toolbar-3">
            <el-button type="primary" style="margin-right: 10px;" @click="convert">{{ i18n.global.t("convert")
                }}</el-button>
            <el-radio-group v-model="convertMode" style="display: none;">
                <el-radio-button value="1" label="Text To Message"></el-radio-button>
                <el-radio-button value="2" label="Message To Text"></el-radio-button>
            </el-radio-group>
            <el-input v-model="txtReg" style="width:300px;margin-left: 10px;display: none;"></el-input>
            <span style="display: inline;margin-left: 10px;color:#444444">{{ i18n.global.t("textConverterTip") }}</span>
            <span style="display: inline;margin-left: 10px;color:var(--el-color-error);float: right;">{{ txtError
                }}</span>
        </el-header>
        <el-main style="float: left;display: flex;flex-direction: row;position: relative;padding:5px">
            <div style="flex-grow: 0;flex-shrink: 0;width:320px">
                <el-input v-model="txtText" :resize="'none'" style="width:100%;" :rows="28" type="textarea" />
            </div>
            <div style="flex-grow: 1; overflow:auto;height: 514px;">
                <el-tree ref="uiTree" class="lib-tree" style="max-width: 600px" :data="treeData" :indent="10"
                    :props="defaultProps" :highlight-current="true" node-key="id" @node-contextmenu="handleContextMenu">
                    <template #default="{ data }">
                        <div style="user-select: none;display: flex;align-items: center;">
                            <TreeDataTypeSvgVue :type="data.tag.type" :format="data.tag.format"
                                :length="data.children == null ? 0 : data.children.length"></TreeDataTypeSvgVue>
                            <span style="margin-left:3px;">{{ data.name }}</span>
                            <span v-if="data.valueString != null && data.valueString != ''" style="margin-left: 4px;"> =
                                {{
                                    data.valueString }}</span>
                            <span v-if="data.desc != null && data.desc.length > 0"
                                style="margin-left:4px; display: inline;color:#555555"> {{
                                    data.desc
                                }}</span>
                        </div>
                    </template>
                </el-tree>
            </div>
        </el-main>
    </el-container>
    <ContextMenuVue ref="contextMenuRef" :items="contextMenuItems"></ContextMenuVue>
</template>